package com.atguigu.gmall.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.order.client.OrderFeignClient;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.payment.config.AlipayConfig;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * title:
 * author: Gyh
 * date:2023-05-10 18:17
 * Description:
 */
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {

    @Autowired
    private OrderFeignClient orderFeignClient;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private AlipayClient alipayClient;

    @Autowired
    private RabbitService rabbitService;

    /**
     * 调用支付宝接口产生支付页面
     * @param orderId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createAlipayForm(Long orderId) {
        try {
            //远程调用订单微服务获取订单信息 验证订单支付状态
            OrderInfo orderInfo = orderFeignClient.getOrderInfoById(orderId);
            if (orderInfo != null && OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus())) {
                //创建本地交易记录对象保存本地交易记录
                PaymentInfo paymentInfo = new PaymentInfo();
                //为本地交易记录封装属性
                paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());
                paymentInfo.setOrderId(orderId);
                paymentInfo.setUserId(orderInfo.getUserId());
                paymentInfo.setPaymentType(PaymentType.ALIPAY.name());
                paymentInfo.setSubject(orderInfo.getTradeBody());
                paymentInfo.setPaymentStatus(PaymentStatus.UNPAID.name());
                //todo 支付金额测试阶段固定设置0.01   支付宝产生支付二维码固定设置0.01
                paymentInfo.setTotalAmount(new BigDecimal(0.01));
                //保存本地交易记录
                paymentInfoService.savePaymentInfo(paymentInfo, PaymentStatus.UNPAID.name());

                //调用支付宝-展示支付页面接口得到渲染支付页面html文本
                AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
                //异步接收地址，仅支持http/https，公网可访问  支付宝平台调用商城系统-用来通知商城支付结果回调地址
                request.setNotifyUrl(AlipayConfig.notify_payment_url);
                //同步跳转地址，仅支持http/https 用户付款后展示支付成功页面
                request.setReturnUrl(AlipayConfig.return_payment_url);
                /******必传参数******/
                JSONObject bizContent = new JSONObject();
                //商户订单号，商家自定义，保持唯一性
                bizContent.put("out_trade_no", orderInfo.getOutTradeNo());
                //支付金额，最小值0.01元
                bizContent.put("total_amount", 0.01);
                //订单标题，不可使用特殊符号
                bizContent.put("subject", orderInfo.getTradeBody());
                //电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
                bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
                /*****`*可选参数******/
                bizContent.put("timeout_express", "10m");
                //扩展信息，按需传入
                JSONObject extendParams = new JSONObject();
                extendParams.put("orderId", orderId);
                bizContent.put("extend_params", extendParams);
                request.setBizContent(bizContent.toString());

                AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
                if (response.isSuccess()) {
                    System.err.println(response.getBody());
                    return response.getBody();
                }
            }
            return null;
        } catch (Exception e) {
            log.error("[支付服务],产生支付宝支付二维码页面异常:{},订单ID:{}", e, orderId);
            throw new RuntimeException(e);
        }
    }

    /**
     * 处理用户支付成功后,支付宝异步通知用户支付结果接口
     * @param paramsMap 支付提交参数
     * @return "success" 支付宝收到该结果,停止通知,如果未响应"success"做到最大努力通知,额外通知7次
     */
    @Override
    public String notifyGmallPayResult(Map<String, String> paramsMap) {
        try {
            //1.业务校验
            //验证签名-保证支付响应数据确实是支付平台,保证提交参数网络传输中没有被篡改.防止出现"假通知" RSA非对称加密
            boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type); //调用SDK验证签名
            if (signVerified) {
                //验证订单是否为商家,通过订单查询订单金额,商户端本地交易记录金额跟支付宝端金额是否一致
                String outTradeNo = paramsMap.get("out_trade_no");
                PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOutTradeNo(outTradeNo, PaymentType.ALIPAY.name());
                // 验证APPId是否为商家的
                if (paymentInfo != null) {
                    BigDecimal totalAmount = paymentInfo.getTotalAmount();
                    String aliPayTotalAmount = paramsMap.get("total_amount");
                    //TODO 前提测试中,保存本地交易记录金额 0.01,生成支付宝支付二维码设置金额 0.01
                    if (totalAmount.compareTo(new BigDecimal(aliPayTotalAmount)) == 0) {
                        //判断支付宝支付交易结果
                        String tradeStatus = paramsMap.get("trade_status");
                        if ("TRADE_SUCCESS".equals(tradeStatus)) {
                            //2.如果支付宝响应支付结果:成功,修改本地交易记录状态
                            this.paySuccess(paymentInfo, paramsMap);
                            return "success";
                        }
                    }
                }
            }
        } catch (AlipayApiException e) {
            return "failure";
        }
        return "failure";
    }

    /**
     * 修改本地交易状态
     * @param paymentInfo
     * @param paramsMap
     */
    private void paySuccess(PaymentInfo paymentInfo, Map<String, String> paramsMap) {
        //更新本地交易记录 跟 支付宝交易记录关联 后续进行日常对账
        //支付宝支付交易编号凭据
        String tradeNo = paramsMap.get("trade_no");
        paymentInfo.setTradeNo(tradeNo);
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(paramsMap.toString());
        //更新本地交易记录状态
        paymentInfo.setPaymentStatus(PaymentStatus.PAID.name());
        paymentInfoService.updateById(paymentInfo);
        //3.发送消息到MQ通知订单微服务修改订单状态
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderId());
    }

    /**
     * 双方协商一致后退款
     * @param orderId
     * @return
     */
    @Override
    public void refund(Long orderId) {
        try {
            //将本地交易记录关闭
            LambdaUpdateWrapper<PaymentInfo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(PaymentInfo::getOrderId, orderId);
            updateWrapper.eq(PaymentInfo::getPaymentType, PaymentType.ALIPAY.name());
            PaymentInfo paymentInfo = paymentInfoService.getOne(updateWrapper);
            if (paymentInfo != null && PaymentStatus.PAID.name().equals(paymentInfo.getPaymentStatus())) {
                //调用支付宝退款接口完成退款处理
                AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
                JSONObject bizContent = new JSONObject();
                if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
                    bizContent.put("trade_no", paymentInfo.getTradeNo());
                }
                bizContent.put("refund_amount", paymentInfo.getTotalAmount());
                bizContent.put("out_request_no", UUID.randomUUID().toString());
                bizContent.put("refund_reason", "双方协商一致,退款!");
                request.setBizContent(bizContent.toString());

                AlipayTradeRefundResponse response = alipayClient.execute(request);
                if (response.isSuccess()) {
                    String fundChange = response.getFundChange();
                    if ("Y".equals(fundChange)) {
                        //退款成功后,修改本地交易记录状态
                        updateWrapper.set(PaymentInfo::getPaymentStatus, PaymentStatus.CLOSED);
                        paymentInfoService.update(updateWrapper);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[支付服务]退款异常:{}, 订单ID:{}", e, orderId);
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭支付宝交易
     * @param orderId
     */
    @Override
    public void closeAliPay(Long orderId) {
        try {
            //根据订单ID+支付方式查询本地交易记录
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderId(orderId, PaymentType.ALIPAY.name());
            //调用支付宝接口关闭交易
            if (paymentInfo!=null){
                AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
                JSONObject bizContent = new JSONObject();
                bizContent.put("out_trade_no",paymentInfo.getOutTradeNo());
                request.setBizContent(bizContent.toString());
                AlipayTradeCloseResponse response = alipayClient.execute(request);
                if (response.isSuccess()){
                    log.info("关闭支付宝订单成功:{}", orderId);
                }else {
                    log.info("关闭支付宝订单失败:{}", orderId);
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            log.error("关闭支付宝订单失败:订单ID:{},异常信息:{}", orderId, e);
        }
    }

    /**
     * 查询支付宝交易订单状态
     * @param orderId
     * @return true 说明该订单等待支付
     */
    @Override
    public Boolean checkPaymentSatus(Long orderId) {
        try {
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfoByOrderId(orderId, PaymentType.ALIPAY.name());
            if (paymentInfo!=null){
                //调用支付宝接口查询交易状态
                AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
                JSONObject bizContent = new JSONObject();
                bizContent.put("out_trade_no",paymentInfo.getOutTradeNo());
                request.setBizContent(bizContent.toString());
                AlipayTradeQueryResponse response = alipayClient.execute(request);
                if (response.isSuccess()){
                    String tradeStatus = response.getTradeStatus();
                    if ("WAIT_BUYER_PAY".equals(tradeStatus)){
                        return true;
                    }
                }
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return false;
    }
}
